﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Xml;
using System.IO;
using System.Globalization;
using System.Xml.Linq;
using System.Windows;
using Microsoft.Win32;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using MockupDesigner.AppItems.Documents;
using MockupDesigner.Items.Metadatas;

namespace MockupDesigner.AppItems
{
    public class DocumentSerializationService
    {
        public DesignerCanvas DesignerCanvas { get; set; }


        public DocumentSerializationService()
        {
        }

        public Document LoadSerializedDataFromFile()
        {
            OpenFileDialog openFile = new OpenFileDialog();
            openFile.Filter = "Designer Files (*.xml)|*.xml|All Files (*.*)|*.*";

            if (openFile.ShowDialog() == true)
            {
                try
                {
                    Document document = new Document();
                    document.DocumentName = openFile.SafeFileName;
                    document.FileName = openFile.FileName;
                    document.IsSaved = true;
                    document.DesignerCanvas = this.DesignerCanvas;

                    XElement root = XElement.Load(openFile.FileName);
                    IEnumerable<XElement> itemsXML = root.Elements("DesignerItems").Elements("DesignerItem");
                    foreach (XElement itemXML in itemsXML)
                    {
                        Guid id = new Guid(itemXML.Element("ID").Value);
                        DesignerItem item = DocumentSerializationService.DeserializeDesignerItem(itemXML, id, 0, 0);
                        if (item != null)
                            this.DesignerCanvas.Children.Add(item);
                    }
                    this.DesignerCanvas.InvalidateVisual();
                    return document;
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }

            return null;
        }

        public void SaveDocument(XElement xElement, Document document)
        {
            if (!document.Exists)
            {
                SaveDocumentAs(xElement, document);
                return;
            }

            try
            {
                xElement.Save(document.FileName);
                document.IsSaved = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }

        }

        public void SaveDocumentAs(XElement xElement, Document document)
        {
            SaveFileDialog saveFile = new SaveFileDialog();
            saveFile.Filter = "Files (*.xml)|*.xml|All Files (*.*)|*.*";
            if (saveFile.ShowDialog() == true)
            {
                try
                {
                    string fileName = saveFile.FileName;
                    document.FileName = fileName;
                    document.DocumentName = saveFile.SafeFileName;
                    xElement.Save(fileName);
                    document.IsSaved = true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        public XElement LoadSerializedDataFromClipBoard()
        {
            if (Clipboard.ContainsData(DataFormats.Xaml))
            {
                String clipboardData = Clipboard.GetData(DataFormats.Xaml) as String;

                if (String.IsNullOrEmpty(clipboardData))
                    return null;
                try
                {
                    return XElement.Load(new StringReader(clipboardData));
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }

            return null;
        }

        public static XElement SerializeDesignerItems(IEnumerable<DesignerItem> designerItems)
        {

            XElement serializedItems = new XElement("DesignerItems",
                                       from item in designerItems
                                       let contentXaml = XamlWriter.Save(((DesignerItem)item).Content)
                                       select new XElement("DesignerItem",
                                                  new XElement("Left", Canvas.GetLeft(item)),
                                                  new XElement("Top", Canvas.GetTop(item)),
                                                  new XElement("Width", item.Width),
                                                  new XElement("Height", item.Height),
                                                  new XElement("ID", item.ID),
                                                  new XElement("zIndex", Canvas.GetZIndex(item)),
                                                  new XElement("IsGroup", item.IsGroup),
                                                  new XElement("ParentID", item.ParentID),
                                                  new XElement("Content", contentXaml)
                                              )
                                   );

            return serializedItems;
        }

        public static DesignerItem DeserializeDesignerItem(XElement itemXML, Guid id, double OffsetX, double OffsetY)
        {
            DesignerItem item = new DesignerItem(id);
            item.Width = Double.Parse(itemXML.Element("Width").Value, CultureInfo.InvariantCulture);
            item.Height = Double.Parse(itemXML.Element("Height").Value, CultureInfo.InvariantCulture);
            item.ParentID = new Guid(itemXML.Element("ParentID").Value);
            item.IsGroup = Boolean.Parse(itemXML.Element("IsGroup").Value);
            Canvas.SetLeft(item, Double.Parse(itemXML.Element("Left").Value, CultureInfo.InvariantCulture) + OffsetX);
            Canvas.SetTop(item, Double.Parse(itemXML.Element("Top").Value, CultureInfo.InvariantCulture) + OffsetY);
            Canvas.SetZIndex(item, Int32.Parse(itemXML.Element("zIndex").Value));
            try
            {
                Object content = XamlReader.Load(XmlReader.Create(new StringReader(itemXML.Element("Content").Value)));
                item.Content = content;
            }
            catch (XamlParseException e)
            {
                MessageBox.Show(string.Format("A problem occured with the Element ID \"{0}\":{1}{2}", item.ID, Environment.NewLine, e.Message), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
            return item;
        }

        public void SerializeDesignerCanvasAsImage(string path)
        {
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
                {
                    Brush originalBG = this.DesignerCanvas.Background;
                    this.DesignerCanvas.Background = Brushes.White;
                    this.DesignerCanvas.SelectionService.ClearSelection();
                    this.DesignerCanvas.UpdateLayout();
                    RenderTargetBitmap bmp = new RenderTargetBitmap((int)this.DesignerCanvas.ActualWidth,
                        (int)this.DesignerCanvas.ActualHeight, 1 / 96, 1 / 96, PixelFormats.Pbgra32);
                    bmp.Render(this.DesignerCanvas);
                    BitmapEncoder encoder = new TiffBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(bmp));
                    encoder.Save(fs);
                    this.DesignerCanvas.Background = originalBG;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }


        public void SerializeMetadatas(string path)
        {
            try
            {
                StringBuilder content = new StringBuilder();
                List<IMetadatable> metadatables = new List<IMetadatable>();
                List<string> metadataNames = new List<string>();
                foreach (DesignerItem di in this.DesignerCanvas.Children.OfType<DesignerItem>())
                {
                    IMetadatable metadatable = di.Content as IMetadatable;
                    if (metadatable == null || metadatable.Metadatas == null)
                        continue;
                    metadatables.Add(metadatable);

                    foreach (Metadata m in metadatable.Metadatas)
                    {
                        if (!metadataNames.Contains(m.Key))
                        {
                            metadataNames.Add(m.Key);
                            content.Append(m.Key);
                            content.Append(';');
                        }
                    }
                }
                if (metadataNames.Count == 0)
                {
                    MessageBox.Show(string.Concat("No Metadata found in this document.", Environment.NewLine, "Export aborted."), "Information", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
                content.Remove(content.Length - 1, 1);
                content.Append(Environment.NewLine);
                foreach (IMetadatable md in metadatables)
                {
                    foreach (string metadataName in metadataNames)
                    {
                        Metadata metadata = md.Metadatas.FirstOrDefault(m => m.Key == metadataName);
                        if (metadata != null)
                            content.Append(metadata.Value);
                        content.Append(';');
                    }
                    content.Remove(content.Length - 1, 1);
                    content.Append(Environment.NewLine);
                }

                using (StreamWriter sw = new StreamWriter(path, false))
                {
                    sw.Write(content.ToString());
                }
            }
            catch (IOException)
            {
                MessageBox.Show("The file is currently used by an other process. It cannot be saved.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}
